﻿using Hu.Core.Domains.Admin.Models;
using Hu.Core.Domains.Admin.Service;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using System.Text.Json;

[ApiController]
[Route("api/[controller]/[action]")]
public class SampleController : ControllerBase
{
    private readonly HuUserService userService;
    private readonly IMemoryCache _memoryCache;
    private readonly IDistributedCache _distributedCache;

    public SampleController(IMemoryCache memoryCache, HuUserService userService, IDistributedCache distributedCache)
    {
        _memoryCache = memoryCache;
        this.userService = userService;
        _distributedCache = distributedCache;
    }

    [HttpGet]
    public IActionResult GetCache()
    {
        //1、普通用法： 判断是否存在缓存，不存在再设置缓存
        const string cacheKey = "sampleKey";
        if (!_memoryCache.TryGetValue(cacheKey, out string? cachedValue))
        {
            // 模拟从数据库或其他服务获取数据
            cachedValue = "This is a sample value11";
            _memoryCache.Set(cacheKey, cachedValue, TimeSpan.FromMinutes(5)); // 缓存5分钟
        }
        return Ok(cachedValue);
    }

    //内存缓存
    /*
     * 缓存穿透
     * 描述：查询一个不存在的值。缓存中没有 数据库中也没有
     * 解决：可以设置一个空的缓存
     * GetOrCreateAsync把null当作是一个合法值
     */

    /*
     * 缓存雪崩
     * 描述：缓存项集中过期引起
     * 解决方法：在基础过期时间之上，再加一个随机的过期时间
     */
    /*
     * 缓存禁止两种类型：IQueryable  IEnumable
     */

    //分布式缓存
    /*
     * 使用参考：GetRedisCache
     * 1、推荐：当内存缓存的集群节点的数量非常多的情况下使用 
     * 2、常用的分布式缓存服务器有Redis、Memcached等
     * Memcached：缓存专用，性能非常高，但是集群、高可用等方面比较弱，而且有“缓存键的最大长度为205字节”等限制。可以安装EnyimMemcachedCore这个第三方NuGet包。
     * Redis：不局限于缓存，Redis做缓存服务器比Memcached性能稍差，但是Redis的高可用、集群等方便非常强大，适合再数据量大、高可用性等场合使用
     * 
     * 3、和内存缓存的区别：缓存值的类型为byte[]，需要我们进行类型转换，也提供了一些按照string类型存取缓存值的扩展方法
     */

    [HttpGet]
    public async Task<IActionResult> GetCache2()
    {
        /*
         * 缓存的key尽量不使用固定值，防止缓存的数据混乱
         * A用户缓存 导致B用户读取到了A用户的信息
         */
        const string cacheKey = "sampleKey";
        //2、GetOrCreate二合一：1、从缓存中取数据 2、从数据源取数据，并且返回给调用者及保存到缓存
        var items = await _memoryCache.GetOrCreateAsync(cacheKey, async (e) =>
        {
            //e.SetAbsoluteExpiration(TimeSpan.FromMinutes(5));//1、设置绝对过期时间，若不设置此选项默认是不过期 除非服务器重启

            //e.SlidingExpiration = TimeSpan.FromMinutes(5); //2、设置滑动过期时间，只要在缓存有效期内请求一次 缓存自动延长一段时间  

            //1、2进行混用 只要在缓存有效期内请求一次缓存自动延长一段时间  若超过绝对过期时间删除此缓存
            //e.SetAbsoluteExpiration(TimeSpan.FromMinutes(30));
            //e.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30);
            e.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(Random.Shared.Next(10, 15));//过期时间随机，防止缓存雪崩
            e.SlidingExpiration = TimeSpan.FromMinutes(5);

            Console.WriteLine("缓存中没找到数据，到数据库中查下看，再进行缓存");
            return await AddUser();//测试错误方法，具体逻辑代码具体实现
        });
        return Ok("cachedValue");
    }

    /// <summary>
    /// Redis缓存Demo
    /// </summary>
    [HttpGet]
    public async Task<ActionResult<HuUser?>> GetRedisCache(string id)
    {
        string cacheKey = "sampleKey" + id;
        /*
         * 需要提前安装Redis服务器
         * 1、安装Nuget包：Microsoft.Extensions.Caching.StackExchangeRedis
         */
        HuUser? data;
        string? cache = await this._distributedCache.GetStringAsync(cacheKey);//从缓存中取
        if (cache == null)
        {
            Console.WriteLine("从数据库中取");
            data = new HuUser
            {
                UserName = "huhu"
            };
            await this._distributedCache.SetStringAsync(cacheKey, JsonSerializer.Serialize(data));
        }
        else
        {
            Console.WriteLine("从缓存中取");
            data = JsonSerializer.Deserialize<HuUser?>(cache);
        }
        if (data == null)
        {
            return NotFound("数据不存在");
        }

        return data;
    }


    [HttpPost("AddUser")]
    public async Task<IActionResult> AddUser()
    {
        HuApiUser user = new HuApiUser
        {
            Email = "163.com",
            Password = "123",
            RealName = "胡胡02",
            UserName = "huhu02"
        };
        await this.userService.AddAsync(user);

        return Ok("成功");
    }


}